package cn.com.stary1993.kite.captcha.support.behavior;

import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.common.exception.ImageCaptchaException;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.validator.ImageCaptchaValidator;
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
import cn.com.stary1993.kite.captcha.config.BehaviorImageCaptchaProperties;
import cn.com.stary1993.kite.captcha.store.CacheStore;
import cn.com.stary1993.kite.captcha.support.CaptchaValidException;
import cn.com.stary1993.kite.captcha.vo.BehaviorCaptchaVO;
import cn.com.stary1993.kite.captcha.vo.CaptchaResponse;
import cn.hutool.core.util.IdUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 默认的 行为图片验证码
 *
 * @author stary1993
 */
@Slf4j
@Data
public class DefaultBehaviorImageCaptcha extends BehaviorImageCaptcha {

	/**
	 * 图片验证码生成器.
	 */
	private ImageCaptchaGenerator template;

	/**
	 * 图片验证码校验器.
	 */
	private ImageCaptchaValidator imageCaptchaValidator;

	/**
	 * 缓冲存储.
	 */
	private CacheStore cacheStore;

	/**
	 * 验证码配置属性.
	 */
	private BehaviorImageCaptchaProperties prop;

	/**
	 * 默认的过期时间.
	 */
	private long defaultExpire = 20000L;

	public DefaultBehaviorImageCaptcha(ImageCaptchaGenerator template, ImageCaptchaValidator imageCaptchaValidator,
			CacheStore cacheStore, BehaviorImageCaptchaProperties prop) {
		this.prop = prop;
		setImageCaptchaTemplate(template);
		setImageCaptchaValidator(imageCaptchaValidator);
		setCacheStore(cacheStore);
		// 默认过期时间
		Long defaultExpire = prop.getExpire().get("default");
		if (defaultExpire != null && defaultExpire > 0) {
			this.defaultExpire = defaultExpire;
		}
	}

	@Override
	public CaptchaResponse<BehaviorCaptchaVO> generate() {
		// 生成滑块验证码
		return generate(CaptchaTypeConstant.SLIDER);
	}

	@Override
	public CaptchaResponse<BehaviorCaptchaVO> generate(String type) {
		ImageCaptchaInfo slideImageInfo = getImageCaptchaTemplate().generateCaptchaImage(type);
		// 重试一次
		if (slideImageInfo == null) {
			slideImageInfo = getImageCaptchaTemplate().generateCaptchaImage(type);
		}
		return afterGenerateSliderCaptcha(slideImageInfo);
	}

	@Override
	public CaptchaResponse<BehaviorCaptchaVO> generate(GenerateParam param) {
		ImageCaptchaInfo slideImageInfo = getImageCaptchaTemplate().generateCaptchaImage(param);
		// 重试一次
		if (slideImageInfo == null) {
			slideImageInfo = getImageCaptchaTemplate().generateCaptchaImage(param);
		}
		return afterGenerateSliderCaptcha(slideImageInfo);
	}

	@Override
	public CaptchaResponse<BehaviorCaptchaVO> generate(BehaviorImageCaptchaFormat captchaImageFormat) {
		return generate(CaptchaTypeConstant.SLIDER, captchaImageFormat);
	}

	@Override
	public CaptchaResponse<BehaviorCaptchaVO> generate(String type, BehaviorImageCaptchaFormat captchaImageFormat) {
		GenerateParam param = new GenerateParam();
		if (BehaviorImageCaptchaFormat.WEBP.equals(captchaImageFormat)) {
			param.setBackgroundFormatName("webp");
			param.setSliderFormatName("webp");
		}
		else {
			param.setBackgroundFormatName("jpeg");
			param.setSliderFormatName("png");
		}
		param.setType(type);
		return generate(param);
	}

	private CaptchaResponse<BehaviorCaptchaVO> afterGenerateSliderCaptcha(ImageCaptchaInfo slideImageInfo) {
		if (slideImageInfo == null) {
			// 要是生成失败
			throw new ImageCaptchaException("生成滑块验证码失败，验证码生成为空");
		}
		// 生成ID
		String id = generatorId();
		// 生成校验数据
		Map<String, Object> validData = getImageCaptchaValidator().generateImageCaptchaValidData(slideImageInfo);
		// 存到缓存里
		cacheVerification(id, slideImageInfo.getType(), validData);
		BehaviorCaptchaVO verificationVO = new BehaviorCaptchaVO();
		verificationVO.setBackgroundImage(slideImageInfo.getBackgroundImage());
		verificationVO.setSliderImage(slideImageInfo.getSliderImage());
		verificationVO.setBackgroundImageWidth(slideImageInfo.getBgImageWidth());
		verificationVO.setBackgroundImageHeight(slideImageInfo.getBgImageHeight());
		verificationVO.setSliderImageWidth(slideImageInfo.getSliderImageWidth());
		verificationVO.setSliderImageHeight(slideImageInfo.getSliderImageHeight());
		verificationVO.setData(slideImageInfo.getData());
		return CaptchaResponse.of(id, verificationVO);
	}

	@Override
	public boolean matching(String id, ImageCaptchaTrack imageCaptchaTrack) {
		Map<String, Object> cachePercentage = getVerification(id);
		if (cachePercentage == null) {
			return false;
		}
		return getImageCaptchaValidator().valid(imageCaptchaTrack, cachePercentage);
	}

	protected String generatorId() {
		return IdUtil.fastUUID();
	}

	/**
	 * 通过缓存获取百分比
	 * @param id 验证码ID
	 * @return Map<String, Object>
	 */
	protected Map<String, Object> getVerification(String id) {
		return (Map<String, Object>) getCacheStore().getAndRemoveCache(getKey(id));
	}

	/**
	 * 缓存验证码
	 * @param id id
	 * @param type
	 * @param validData validData
	 */
	protected void cacheVerification(String id, String type, Map<String, Object> validData) {
		Long expire = prop.getExpire().getOrDefault(type, defaultExpire);
		if (!getCacheStore().setCache(getKey(id), validData, expire, TimeUnit.MILLISECONDS)) {
			log.error("缓存验证码数据失败， id={}, validData={}", id, validData);
			throw new CaptchaValidException(type, "缓存验证码数据失败");
		}
	}

	protected String getKey(String id) {
		return prop.getKeyPrefix().concat(":").concat(id);
	}

	@Override
	public ImageCaptchaResourceManager getImageCaptchaResourceManager() {
		return getImageCaptchaTemplate().getImageResourceManager();
	}

	@Override
	public ImageCaptchaValidator getImageCaptchaValidator() {
		return this.imageCaptchaValidator;
	}

	@Override
	public void setImageCaptchaValidator(ImageCaptchaValidator imageCaptchaValidator) {
		this.imageCaptchaValidator = imageCaptchaValidator;
	}

	@Override
	public ImageCaptchaGenerator getImageCaptchaTemplate() {
		return this.template;
	}

	@Override
	public void setImageCaptchaTemplate(ImageCaptchaGenerator imageCaptchaGenerator) {
		this.template = imageCaptchaGenerator;
	}

	@Override
	public CacheStore getCacheStore() {
		return this.cacheStore;
	}

	@Override
	public void setCacheStore(CacheStore cacheStore) {
		this.cacheStore = cacheStore;
	}

}
